"""
定时任务调度器 - 每5分钟更新数据库
"""
import asyncio
import logging
from datetime import datetime, timedelta, date
from typing import List, Dict, Any
import time
from decimal import Decimal

from services.fear_greed_service import fear_greed_service
from .service import db_service
from .models import StockData, DataUpdateLog

logger = logging.getLogger(__name__)

class DataScheduler:
    """数据定时更新调度器"""
    
    def __init__(self):
        # 使用全局服务实例
        self.running = False
        self.update_interval = 300  # 5分钟 = 300秒
        
    async def initialize(self):
        """初始化调度器"""
        try:
            # 使用全局服务实例，无需初始化
            logger.info("数据调度器初始化成功")
            return True
        except Exception as e:
            logger.error(f"数据调度器初始化失败: {e}")
            return False
    
    async def start(self):
        """启动定时任务"""
        if self.running:
            logger.warning("调度器已在运行中")
            return
        
        # 初始化调度器
        if not await self.initialize():
            logger.error("调度器初始化失败，无法启动")
            return
        
        self.running = True
        logger.info(f"数据调度器启动，更新间隔: {self.update_interval}秒")
        
        while self.running:
            try:
                await self._run_update_cycle()
                
                # 等待下一次更新
                if self.running:
                    await asyncio.sleep(self.update_interval)
                    
            except Exception as e:
                logger.error(f"调度器运行异常: {e}")
                if self.running:
                    await asyncio.sleep(60)  # 出错时等待1分钟再重试
    
    def stop(self):
        """停止定时任务"""
        self.running = False
        logger.info("数据调度器停止")
    
    async def _run_update_cycle(self):
        """执行一轮更新周期"""
        start_time = time.time()
        
        # 获取所有需要更新的股票代码
        all_symbols = await self._get_all_symbols()
        
        logger.info(f"开始更新周期，共 {len(all_symbols)} 只股票")
        
        # 分批更新，避免过度占用资源
        batch_size = 10
        for i in range(0, len(all_symbols), batch_size):
            batch_symbols = all_symbols[i:i + batch_size]
            await self._update_batch(batch_symbols)
            
            # 批次间短暂休息
            if i + batch_size < len(all_symbols):
                await asyncio.sleep(1)
        
        execution_time = time.time() - start_time
        logger.info(f"更新周期完成，耗时: {execution_time:.2f}秒")
    
    async def _get_all_symbols(self) -> List[str]:
        """获取所有需要更新的股票代码"""
        try:
            symbols = set()
            
            # 获取各分类的股票
            for category in ['leverage', 'regular', 'individual']:
                category_symbols = await db_service.get_stocks_by_category(category)
                symbols.update(category_symbols)
            
            return list(symbols)
        except Exception as e:
            logger.error(f"获取股票代码列表失败: {e}")
            return []
    
    async def _update_batch(self, symbols: List[str]):
        """批量更新股票数据"""
        tasks = []
        for symbol in symbols:
            task = asyncio.create_task(self._update_single_stock(symbol))
            tasks.append(task)
        
        # 等待所有任务完成
        await asyncio.gather(*tasks, return_exceptions=True)
    
    async def _update_single_stock(self, symbol: str):
        """更新单只股票的数据"""
        try:
            # 检查数据是否需要更新
            if not await self._should_update_stock(symbol):
                logger.debug(f"股票 {symbol} 数据较新，跳过更新")
                return
            
            # 更新股票基础信息
            await self._update_stock_info(symbol)
            
            # 更新日线数据
            await self._update_daily_data(symbol)
            
            # 更新恐贪指数
            await self._update_fear_greed_data(symbol)
            
            logger.debug(f"股票 {symbol} 数据更新完成")
            
        except Exception as e:
            logger.error(f"更新股票 {symbol} 数据失败: {e}")
            # 记录错误日志
            await self._log_update_error(symbol, str(e))
    
    async def _should_update_stock(self, symbol: str) -> bool:
        """检查股票是否需要更新"""
        try:
            # 检查K线数据新鲜度（超过4小时需要更新）
            kline_stale = await db_service.is_data_stale(symbol, 'kline', 4)
            
            # 检查恐贪指数新鲜度（超过4小时需要更新）
            fear_greed_stale = await db_service.is_data_stale(symbol, 'fear_greed', 4)
            
            return kline_stale or fear_greed_stale
        except Exception as e:
            logger.error(f"检查股票 {symbol} 更新需求失败: {e}")
            return True  # 出错时默认需要更新
    
    async def _update_stock_info(self, symbol: str):
        """更新股票基础信息"""
        try:
            # 这里可以通过长桥API获取股票基础信息
            # 暂时使用简单的市场判断
            market = 'US' if '.US' in symbol else 'HK' if '.HK' in symbol else 'CN'
            name = symbol.replace('.US', '').replace('.HK', '').replace('.SZ', '').replace('.SH', '')
            
            stock_info = StockData(
                symbol=symbol,
                timestamp=datetime.now(),
                data_type='basic_info',
                name=name,
                exchange='NASDAQ' if market == 'US' else 'HKEX' if market == 'HK' else 'SSE',
                sector='Technology'
            )
            await db_service.save_stock_data([stock_info])
            
        except Exception as e:
            logger.error(f"更新股票 {symbol} 基础信息失败: {e}")
    
    async def _update_daily_data(self, symbol: str):
        """更新股票日线数据"""
        start_time = time.time()
        records_updated = 0
        
        try:
            # 获取最近30天的数据
            df = await fear_greed_service.get_stock_data(symbol, 30)
            
            if df.empty:
                logger.warning(f"股票 {symbol} 无法获取日线数据")
                return
            
            # 转换为数据库模型
            kline_data_list = []
            for date_idx, row in df.iterrows():
                kline_data = StockData(
                    symbol=symbol,
                    timestamp=date_idx,
                    data_type='kline',
                    open=Decimal(str(row['Open'])),
                    high=Decimal(str(row['High'])),
                    low=Decimal(str(row['Low'])),
                    close=Decimal(str(row['Close'])),
                    volume=int(row['Volume']),
                    turnover=Decimal(str(row.get('Turnover', 0)))
                )
                kline_data_list.append(kline_data)
            
            # 保存到数据库
            records_updated = await db_service.save_stock_data(kline_data_list)
            
            # 记录更新日志
            execution_time = time.time() - start_time
            log = DataUpdateLog(
                symbol=symbol,
                update_type='kline',
                start_date=kline_data_list[-1].timestamp.date() if kline_data_list else None,
                end_date=kline_data_list[0].timestamp.date() if kline_data_list else None,
                records_updated=records_updated,
                status='success',
                execution_time=Decimal(str(execution_time))
            )
            await db_service.save_update_log(log)
            
        except Exception as e:
            logger.error(f"更新股票 {symbol} K线数据失败: {e}")
            await self._log_update_error(symbol, str(e), 'kline')
    
    async def _update_fear_greed_data(self, symbol: str):
        """更新恐贪指数数据"""
        start_time = time.time()
        records_updated = 0
        
        try:
            # 从数据库获取股票数据
            df = await db_service.get_stock_data_as_dataframe(symbol, 100)
            
            if df.empty or len(df) < 20:
                logger.warning(f"股票 {symbol} 数据不足，无法计算恐贪指数")
                return
            
            # 计算最近30天的恐贪指数
            fear_greed_list = []
            for i in range(max(20, len(df) - 30), len(df)):
                daily_data = df.iloc[:i+1].copy()
                if len(daily_data) >= 20:
                    try:
                        result = fear_greed_service.processor.calculate_fear_greed_index_from_data(daily_data, symbol)
                        
                        fear_greed = StockData(
                            symbol=symbol,
                            timestamp=daily_data.index[-1],
                            data_type='fear_greed',
                            fear_greed_index=Decimal(str(result['fear_greed_index'])),
                            sentiment=result['sentiment'],
                            investment_advice=result['investment_advice'],
                            close=Decimal(str(daily_data['Close'].iloc[-1])),
                            rsi=Decimal(str(50.0)),  # 默认值，可以从计算中获取
                            volatility=Decimal(str(0.0)),  # 默认值
                            volume_ratio=Decimal(str(1.0)),  # 默认值
                            momentum_score=Decimal(str(result['components']['momentum'])),
                            volatility_score=Decimal(str(result['components']['volatility'])),
                            volume_score=Decimal(str(result['components']['volume'])),
                            put_call_score=Decimal(str(result['components']['put_call_ratio']))
                        )
                        fear_greed_list.append(fear_greed)
                        
                    except Exception as e:
                        logger.error(f"计算股票 {symbol} 第 {i} 天恐贪指数失败: {e}")
            
            # 保存到数据库
            if fear_greed_list:
                records_updated = await db_service.save_stock_data(fear_greed_list)
            
            # 记录更新日志
            execution_time = time.time() - start_time
            log = DataUpdateLog(
                symbol=symbol,
                update_type='fear_greed',
                start_date=fear_greed_list[-1].timestamp.date() if fear_greed_list else None,
                end_date=fear_greed_list[0].timestamp.date() if fear_greed_list else None,
                records_updated=records_updated,
                status='success',
                execution_time=Decimal(str(execution_time))
            )
            await db_service.save_update_log(log)
            
        except Exception as e:
            logger.error(f"更新股票 {symbol} 恐贪指数失败: {e}")
            await self._log_update_error(symbol, str(e), 'fear_greed')
    
    async def _log_update_error(self, symbol: str, error_message: str, update_type: str = 'kline'):
        """记录更新错误日志"""
        try:
            log = DataUpdateLog(
                symbol=symbol,
                update_type=update_type,
                status='failed',
                error_message=error_message,
                records_updated=0
            )
            await db_service.save_update_log(log)
        except Exception as e:
            logger.error(f"记录错误日志失败: {e}")

# 全局调度器实例
data_scheduler = DataScheduler()
